package com.example.edu.Service.Impl;

import cn.hutool.json.JSONUtil;
import com.example.edu.Dto.*;
import com.example.edu.Entity.Chat_Record;
import com.example.edu.Entity.History;
import com.example.edu.Entity.User;
import com.example.edu.Mapper.*;
import com.example.edu.Service.AIService;
import com.example.edu.Utils.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhipu.oapi.Constants;
import io.reactivex.rxjava3.core.Flowable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.example.edu.Utils.MyConstants.*;

@Service
public class AIServiceImpl implements AIService {
    @Resource
    CourseMapper courseMapper;
    @Resource
    ChapterMapper chapterMapper;
    @Resource
    DocumentMapper documentMapper;
    @Resource
    Mind_MapMapper mind_mapMapper;
    @Resource
    UserMapper userMapper;
    @Resource
    HistoryMapper historyMapper;
    @Resource
    Chat_RecordMapper chatRecordMapper;
    @Resource
    Chat_FilesMapper chatFilesMapper;
    @Resource
    CacheDataBaseUtil cacheDataBaseUtil;
    @Resource
    AIUtil aiUtil;
    @Resource
    EmbeddingUtil embeddingUtil;
    @Resource
    EsUtil esUtil;
    private static final Logger logger = LoggerFactory.getLogger(AIServiceImpl.class);

    @Override
    public Return historyList(HttpServletRequest request) {
        String token = JwtUtil.getTokenFromCookie(request);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        List<History> histories = historyMapper.getHistories(user.getUser_id());
        if (histories.isEmpty())
            return Return.ok("还没有历史记录",histories);
        return Return.ok(histories);
    }

    @Override
    @Transactional
    public Return update(History history) {
        try {
            return update1(history);
        } catch (Exception e){
            return Return.fail(1,"修改失败");
        }
    }

    @Override
    @Transactional
    public Return delete(Integer historyId) {
        try {
            return delete1(historyId);
        } catch (Exception e){
            return Return.fail(1,"删除失败");
        }
    }

    @Override
    @Transactional
    public Flowable<String> chat(AIChatDtoG aiChatData, HttpServletRequest request) {
        String token = JwtUtil.getTokenFromCookie(request);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        String question = aiChatData.getQuestion();
        Integer history_id;
        History history;
        List<String> user_messages = new ArrayList<>();
        List<String> system_messages = new ArrayList<>();
        List<String> assistant_message = new ArrayList<>();
        Flowable<String> aiStream;
        Flowable<String> historyStream = Flowable.empty();
        system_messages.add(SYSTEM_PROMPT3);
        if(aiChatData.getCourse_id() != null)
            addCourseMessage(system_messages,aiChatData.getCourse_id());
        if(aiChatData.getChapter_id() != null)
            addChapterMessage(system_messages,aiChatData.getChapter_id());
        if(aiChatData.getDocument_id() != null){
            addDocumentMessage(system_messages,aiChatData.getDocument_id());
        }
        if(aiChatData.getIs_rag() == 1){
            addRAGMessage(question,system_messages,aiChatData.getIs_think() == 0 ? 10 : 5);
        }
        if (aiChatData.getHistory_id() != null) {
            history_id = aiChatData.getHistory_id();
            assistant_message = addHistoryMessage(user_messages,history_id,aiChatData.getIs_think() == 0 ? 6 : 4);
        }
        else {
            String history_name = question;
            if (history_name.length() > HISTORY_NAME_MAX_LENGTH)
                history_name = history_name.substring(0, HISTORY_NAME_MAX_LENGTH);
            history = new History(0, history_name, user.getUser_id(),0);
            historyMapper.add(history);
            history_id = history.getHistory_id();
            historyStream = Flowable.just(JSONUtil.toJsonStr(history));
        }
        user_messages.add(question);
        PromptDto prompt = new PromptDto(user_messages, system_messages, assistant_message,aiChatData.getIs_web() == 0 ? null : question);
        StringBuilder aiResponse = new StringBuilder();
        if(aiChatData.getIs_think() == 0)
            aiStream = aiUtil.getAnswerSSE(prompt, AI_REQUEST_ID+history_id.toString(), Constants.ModelChatGLM4Plus);
        else
            aiStream = aiUtil.getAnswerSSE(prompt, AI_REQUEST_ID+history_id.toString(), AI_MODEL_CODE[2]);
        if(aiChatData.getHistory_id() != null)
            return aiStream.doOnNext(aiResponse::append).doOnComplete(() -> {
            chatRecordMapper.addChats(history_id, question, aiResponse.toString());
        });
        return Flowable.concat(
                historyStream, // 先返回 history JSON
                aiStream.doOnNext(aiResponse::append) // 再返回 AI 生成的内容
        ).doOnComplete(() -> {
            addChats(history_id,question,aiResponse.toString());
        });
    }

    @Override
    public Return chatList(ChatListDtoG chatListData) {
        int total = chatRecordMapper.getChatNum(chatListData.getHistory_id());
        if(total == 0 || chatListData.getStart_index() >= total)
            return Return.ok("还没有聊天记录",new ListDtoR(total,new ArrayList<>()));
        List<ChatListDtoR> chats = chatRecordMapper.getChats(chatListData.getHistory_id(),chatListData.getStart_index(),Math.min(chatListData.getCount(),total-chatListData.getStart_index()));
        if (chats.isEmpty())
            return Return.ok("这一页还没有聊天记录",new ListDtoR(total,new ArrayList<>()));
        for(ChatListDtoR chat : chats){
            List<String> urls = chatFilesMapper.getUrls(chat.getChat_id());
            chat.setUrls(urls);
        }
        return Return.ok(new ListDtoR(total,chats));
    }

    @Override
    public Return historyListM(HttpServletRequest request) {
        String token = JwtUtil.getTokenFromCookie(request);
        String telephone = JwtUtil.getUsername(token);
        User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                USER_CACHE_TTL, TimeUnit.HOURS);
        List<History> histories = historyMapper.getHistoriesM(user.getUser_id());
        if (histories.isEmpty())
            return Return.ok("还没有历史记录",histories);
        return Return.ok(histories);
    }

    @Override
    public Return addRecord(AddRecordDtoG records) {
        List<RecordDto> recordDataList = records.getChats();
        try {
            for (RecordDto addRecordData : recordDataList){
                chatRecordMapper.addChatsM(addRecordData);
                if(!addRecordData.getUrls().isEmpty())
                    chatFilesMapper.addUrls(addRecordData.getHistory_id(),addRecordData.getChat_id(),addRecordData.getUrls());
            }
            return Return.ok();
        } catch (Exception e){
            logger.error(e.getMessage());
            return Return.fail(1,"添加失败");
        }
    }

    @Override
    public Return addHistory(HttpServletRequest request, History history) {
        try {
            String token = JwtUtil.getTokenFromCookie(request);
            String telephone = JwtUtil.getUsername(token);
            User user = cacheDataBaseUtil.selectById(USER_KEY,telephone, User.class, telephone2->userMapper.getUser(telephone2),
                    USER_CACHE_TTL, TimeUnit.HOURS);
            history.setHistory_user(user.getUser_id());
            historyMapper.addM(history);
            return Return.ok(history.getHistory_id());
        } catch (Exception e){
            logger.error(e.getMessage());
            return Return.fail(1,"添加失败");
        }
    }

    @Transactional
    public Return update1(History history){
        try {
            historyMapper.update(history);
            return Return.ok();
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    @Transactional
    public Return delete1(Integer historyId) {
        try {
            historyMapper.delete(historyId);
            chatRecordMapper.delete(historyId);
            chatFilesMapper.deleteUrls(historyId);
            return Return.ok();
        } catch (Exception e){
            logger.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    private List<String> addHistoryMessage(List<String> userMessages, Integer history_id, int num) {
        List<Chat_Record> histories = chatRecordMapper.getHistoryChats(history_id,num);
        List<String> assistant_message = new ArrayList<>();
        for(int i = histories.size()-1;i >= 0;i--){
            Chat_Record history = histories.get(i);
            if(history.getChat_type() == 0)
                assistant_message.add(history.getContent());
            else
                userMessages.add(history.getContent());
        }
        return assistant_message;
    }

    private void addCourseMessage(List<String> system_messages,int course_id){
        String course_name = courseMapper.getCourseName(course_id);
        system_messages.add(String.format(AI_CHAT_SYSTEM_PROMPT4,course_name));
        String mind_map = cacheDataBaseUtil.selectById2(MIND_MAP_KEY, course_id,String.class, course_id2 -> mind_mapMapper.getMap(course_id2));
        if (mind_map != null)
            system_messages.add(AI_CHAT_SYSTEM_PROMPT6 + mind_map);
    }

    private void addChapterMessage(List<String> system_messages,int chapter_id){
        ChatMessageDto chatMessageData = chapterMapper.getMessageByChapterId(chapter_id);
        system_messages.add(String.format(AI_CHAT_SYSTEM_PROMPT4,chatMessageData.getCourse_name()) + "\n" + String.format(AI_CHAT_SYSTEM_PROMPT5,chatMessageData.getChapter_name()));
        String mind_map = cacheDataBaseUtil.selectById2(MIND_MAP_KEY, chatMessageData.getCourse_id(),String.class, course_id -> mind_mapMapper.getMap(course_id));
        if (mind_map != null)
            system_messages.add(AI_CHAT_SYSTEM_PROMPT6 + mind_map);
    }

    private void addDocumentMessage(List<String> system_messages,int document_id){
        ChatMessageDto chatMessageData = documentMapper.getMessageByDocumentId(document_id);
        system_messages.add(String.format(AI_CHAT_SYSTEM_PROMPT4,chatMessageData.getCourse_name()) + "\n" + String.format(AI_CHAT_SYSTEM_PROMPT5,chatMessageData.getChapter_name()));
        String path = DOCUMENT_ADDRESS + "/" + document_id + "." + chatMessageData.getDocument_type();
        try {
            String content = readFile(path);
            if(content.isEmpty())
                return;
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode root = objectMapper.readTree(content).get("root");
            JsonNode children = root.get("children");
            String text = null;
            if (children != null && children.isArray())
                text = RegexUtils.extractText(children);
            if(text != null)
                system_messages.add(AI_CHAT_SYSTEM_PROMPT + text);
            String mind_map = cacheDataBaseUtil.selectById2(MIND_MAP_KEY, chatMessageData.getCourse_id(),String.class, course_id -> mind_mapMapper.getMap(course_id));
            if (mind_map != null)
                system_messages.add(AI_CHAT_SYSTEM_PROMPT3 + mind_map);
        } catch (IOException e){
            logger.error(e.getMessage());
        }
    }

    private void addRAGMessage(String question, List<String> system_messages,int num) {
        List<String> questions = new ArrayList<>();
        questions.add(question);
        List<List<Double>> embedding = embeddingUtil.getEmbeddings(questions);
        try {
            List<ESContentDto> similarityContents = esUtil.searchByEmbedding(embedding.get(0),num);
            List<ESContentDto> similarityContents2 = similarityContents.stream().filter(similarityContent -> similarityContent.getSimilarityScore() > 0.6).collect(Collectors.toList());
            if(!similarityContents2.isEmpty()) {
                StringBuilder similarityContentMessage = new StringBuilder(AI_CHAT_SYSTEM_PROMPT2);
                for(ESContentDto similarityContent : similarityContents2)
                    similarityContentMessage.append(similarityContent.getContent()).append("\n\n");
                system_messages.add(similarityContentMessage.toString());
            }
        } catch (IOException e){
            logger.error(e.getMessage());
        }
    }

    @Async
    protected void addChats(int history_id,String question,String response){
        chatRecordMapper.addChats(history_id, question, response);
    }

    private String readFile(String path) throws IOException {
        return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
    }
}
